package com.mytest.provider.es.serviceImpl;

import com.google.common.collect.Maps;
import com.myrest.provider.poi.work.entities.user.ESUser;
import com.myrest.provider.poi.work.entities.user.NickName;
import com.myrest.provider.poi.work.entities.user.Sex;
import com.myrest.provider.poi.work.entities.user.nickAndSex;
import com.mytest.provider.es.clients.userFen;
import com.mytest.provider.es.config.EsConstant;
import com.mytest.provider.es.mapper.ElasticRepository;
import com.mytest.provider.es.utils.SearchRequest;
import com.mytest.provider.es.utils.SearchResult;
import com.sun.org.apache.bcel.internal.generic.NEW;
import org.apache.commons.lang3.StringUtils;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.Operator;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.bucket.terms.LongTerms;
import org.elasticsearch.search.aggregations.bucket.terms.StringTerms;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.data.elasticsearch.core.aggregation.AggregatedPage;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.stereotype.Service;

import java.util.*;


@Service
public class SearchService {
    @Autowired
    private ElasticRepository elasticRepository;
    @Autowired
    private com.mytest.provider.es.clients.userFen userFen;

    /**
     * 条件 + 过滤 + 分页 + 聚合
     * @param searchRequest
     * @return
     */
    public SearchResult page(SearchRequest searchRequest) {
//        查询条件构造器
        NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();

//        查询条件
        String key = searchRequest.getKey();
        if (StringUtils.isBlank(key)){
            return null;
        }
        //调用过滤查询方法,返回一个bool的查询条件对象
        QueryBuilder query = buildBasicQueryWithFilter(searchRequest);

//       添加查询条件  AND匹配度必须完全一致,才能查询到,即不可模糊查询fuzzy
        queryBuilder.withQuery(query);

//       多索引查询 ？？
//        queryBuilder.withIndices(EsConstant.INDEX_NAME.TRAVEL);

//        preTags 高亮开始  postTags 高亮结束
        /*List<String> highlightFields = new ArrayList<String>();
        highlightFields.add("title");
        highlightFields.add("passCity");
        highlightFields.add("description");
        highlightFields.add("content");
        highlightFields.add("keyword");
        Field[] fields = new Field[highlightFields.size()];
        for (int x = 0; x < highlightFields.size(); x++) {
            HighlightBuilder.Field field = new HighlightBuilder.Field(highlightFields.get(x)).preTags(EsConstant.HIGH_LIGHT_START_TAG)
                    .postTags(EsConstant.HIGH_LIGHT_END_TAG);
        }
//        高亮条件
        queryBuilder.withHighlightFields(fields);*/


//        添加分页条件
        queryBuilder.withPageable(PageRequest.of(searchRequest.getPage()-1,searchRequest.getSize()));

//--------对昵称和性别聚合分桶--------

        String nickname ="nickNames";
        String sex ="sexs";
        //添加分桶的条件 ,根据下面来理解
        /*GET /cars/_search
        {
            "size" : 0,                            //查询条数,0为不查询,不关心搜索到的数据，只关心聚合结果，提高效率
            "aggs" : {                              //AggregationBuilders
            "popular_colors" : {                    //随便取的聚合名称
                "terms" : {                     //AggregationBuilders.terms 分桶的方式,terms按词条分组
                    "field" : "color"           //field("nickId") 条件,对那个字段进行分组
                }
            }
        }
        }*/
        queryBuilder.addAggregation(AggregationBuilders.terms(nickname).field("nickId"));
        queryBuilder.addAggregation(AggregationBuilders.terms(sex).field("sexId"));
        List<NickName> nicknames=new ArrayList<>();
        List<Sex> sexs=new ArrayList<>();

        //        搜索的结果  ,将查询条件构造器进行build构造,变为查询条件对象
        AggregatedPage<ESUser> search = (AggregatedPage<ESUser>) this.elasticRepository.search(queryBuilder.build());


        //获取昵称聚合的结果
        /*aggregations": {                                   //聚合的结果
        "group_by_tags": {                                   //search.getAggregation(nickname) 我们起的聚合的名称
            "doc_count_error_upper_bound": 0,
                    "sum_other_doc_count": 0,
                    "buckets": [                             //nickterms.getBuckets() 分桶结果不止一个 集合
            {
                "key": "fangzhu",                            //bucket.getKeyAsString()  每个桶对应的名称,分桶的字段是String
                "doc_count": 2                               //桶里面的数据的数量,默认降序排序
            }
        }*/
        LongTerms nickterms = (LongTerms) search.getAggregation(nickname);
        ArrayList<Long> nickIds = new ArrayList<>();
        //从昵称所有的聚合分桶中获取所有的昵称id信息,   Number是数字类型 .longValue()
        nickterms.getBuckets().forEach(bucket -> nickIds.add(bucket.getKeyAsNumber().longValue()));
//        List<Brand> brands = this.brandClient.queryBrandByIds(brandIds);
        nickIds.forEach(nickId->{
//            NickName nickName = this.brandClient.queryBrandById(brandId);
            Map<String, Object> map = Maps.newHashMap();
            map.put("id",nickId);
            NickName nickName = userFen.selectNick(map);
            nicknames.add(nickName);
        });


        //获取性别聚合的结果
        LongTerms sexterms = (LongTerms) search.getAggregation(sex);
        List<Long> sexIds = new ArrayList<>();
        //获取所有桶的集合
        List<LongTerms.Bucket> buckets = sexterms.getBuckets();
        //从分类所有的聚合分桶中获取所有的分类id信息
        buckets.forEach(bucket -> sexIds.add(bucket.getKeyAsNumber().longValue()));
        //根据id获取性别名字
        List<Integer> sexnames = userFen.selectSexByIds(sexIds);

        for (int i=0;i<sexIds.size();i++){
            Sex sex1 = new Sex();
            sex1.setId(sexIds.get(i));
            sex1.setSex(sexnames.get(i));
            sexs.add(sex1);
        }

//        ----------------------
//        规格参数
        List<Map<String,Object>> specs=null;
//        当性别唯一时,才展示规格参数
        if(sexs.size()==1){
//            获取规格参数,根据规格参数的id
            specs=getSpecs(sexs.get(0).getId(),query);

        }
         /*
         * search.getNumber()   聚合分桶的 分页的页数
         * search.getSize()   聚合分桶的 分页的每页数量
         * search.getTotalPages()  聚合分桶的 分页的总数
         * sexs                聚合分桶的聚合名称为sex的 当前分页的key的集合
         * */
        return new SearchResult(search.getNumber(),search.getSize(),search.getTotalPages(),sexs,nicknames,specs);

//        return new SearchResult(search.getTotalElements(),new Long(search.getTotalPages()),search.getContent(),sexs,nicknames,specs);
    }

    /**
     * 对查询到的数据进行可搜索规格参数的聚合操作,聚合时要和搜索条件相关,搜到什么内容,聚合什么,不要全部聚合
     * @param id
     * @return
     */
    private List<Map<String,Object>> getSpecs(Long id,QueryBuilder query) {    //加了query可搜索规格参数的聚合的查询条件
        //存储返回的结果
        List<Map<String,Object>> speclist = new ArrayList<>();

        NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();

        //先根据查询条件,执行查询
        queryBuilder.withQuery(query);

        //对规格参数聚合,要拿到所有的可以搜索的规格参数
        List<nickAndSex> searchingnickAndSexParams = this.userFen.selectNickAndSexBySexId(id);
        //添加聚合条件
        searchingnickAndSexParams.forEach(specParam -> queryBuilder.addAggregation(
                AggregationBuilders.terms(specParam.getName()).field("specs."+specParam.getName()+".keyword")));

        AggregatedPage<ESUser> page = (AggregatedPage<ESUser>) this.elasticRepository.search(queryBuilder.build());
        searchingnickAndSexParams.forEach(specParam -> {
            String name = specParam.getName();

            StringTerms stringTerms = (StringTerms)page.getAggregation(name);
            List<String> values = new ArrayList<>();
            List<StringTerms.Bucket> buckets = stringTerms.getBuckets();
            //把聚合分桶中每个值存入values集合中
            buckets.forEach(bucket -> values.add(bucket.getKeyAsString()));


            Map<String,Object> specmap = new HashMap<>();
            specmap.put("key",name);  //key-->品牌
            specmap.put("obtions",values); //obtions-->[品牌名称...]
            speclist.add(specmap);
        });

        return speclist;
    }

    // 构建基本查询条件,过滤 ***就是构建查询条件
    private QueryBuilder buildBasicQueryWithFilter(SearchRequest request) {
//        NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();

        //创建bool查询
        //结合下面来理解 : 在title == 大米的值里面过滤 一定要过滤 key == value的值
        // 这里的结构是 布尔{分词匹配,过滤{布尔{精确}}}
        /*GET /xxx/_search
        {
            "query":{                             //QueryBuilders
            "bool":{                              //QueryBuilders.boolQuery()
                "must":{                          //queryBuilder.must
                    "match":{                     //QueryBuilders.matchQuery
                        "title":"大米"            //"all", request.getKey()).operator(Operator.AND)
                    }
                },
                "filter":{                        //queryBuilder.filter  过滤是构建在bool查询中的,是对查询结果进行过滤,不会影响得分
                     "bool":{
                         "must":{
                             "term":{
                             "key":value}         //一定要过滤 key == value的值
                      }}
        	}
            }}
        }*/
        BoolQueryBuilder queryBuilder = QueryBuilders.boolQuery();
        // 基本查询条件         搜索
        // must 一定要 must not 一定不要  should可以要
        // 由于 在索引库里面对all字段进行了 ik分词,所以这里有一个匹配度
        // Operator.AND属性，指的是必须要我传入的查询条件分词后字段全部匹配，才会返回结果。默认是Operator.OR，也就是其中有一个匹配，结果就返回
        queryBuilder.must(QueryBuilders.matchQuery("all", request.getKey()).operator(Operator.AND));

        // bool查询
        BoolQueryBuilder filterQueryBuilder = QueryBuilders.boolQuery();
        // 整理过滤条件
        Map<String, String> filter = request.getFilter();
        for (Map.Entry<String, String> entry : filter.entrySet()) {
            String key = entry.getKey();
            String value = entry.getValue();
            // 昵称和性别可以直接查询不需要拼接
            if (key != "cid3" && key != "brandid") {
                //esql查询时,必须以这种查询
                key = "specs." + key + ".keyword";
            }
            // 字符串类型，进行term查询,这里没有匹配度,因为是精确匹配查询
            filterQueryBuilder.must(QueryBuilders.termQuery(key, value));
        }
        // 添加过滤条件
        queryBuilder.filter(filterQueryBuilder);
        return queryBuilder;
    }

}
